<!DOCTYPE html>
<html lang="zh-CN">
<head hexo-theme='https://github.com/volantis-x/hexo-theme-volantis/tree/4.1.5'>
  <meta charset="utf-8">
  <!-- SEO相关 -->
  
    
  
  <!-- 渲染优化 -->
  <meta http-equiv='x-dns-prefetch-control' content='on' />
  <link rel='dns-prefetch' href='https://cdn.jsdelivr.net'>
  <link rel="preconnect" href="https://cdn.jsdelivr.net" crossorigin>
  <meta name="renderer" content="webkit">
  <meta name="force-rendering" content="webkit">
  <meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">
  <meta name="HandheldFriendly" content="True" >
  <meta name="apple-mobile-web-app-capable" content="yes">
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">

  <!-- 页面元数据 -->
  
  <title>前端面试题 - Alan|前端博客</title>
  
    <meta name="keywords" content="面试题">
  

  
    <meta name="description" content="记录了自己疫情期间自学+复习总结的一些面试题+面试遇到的一些问题，文章将会持续更新。">
  

  <!-- feed -->
  

  <!-- import meta -->
  

  <!-- link -->
  
    <link rel="shortcut icon" type='image/x-icon' href="https://gitee.com/alannum3/MyImages/raw/master/img/basketball-ball.png">
  

  <!-- import link -->
  

  
    
<link rel="stylesheet" href="/css/first.css">

  

  
  <link rel="stylesheet" href="/css/style.css" media="print" onload="this.media='all';this.onload=null">
  <noscript><link rel="stylesheet" href="/css/style.css"></noscript>
  

  <script id="loadcss"></script>

</head>

<body>
  

<header id="l_header" class="l_header auto shadow blur " style='opacity: 0' >
  <div class='container'>
  <div id='wrapper'>
    <div class='nav-sub'>
      <p class="title"></p>
      <ul class='switcher nav-list-h m-phone' id="pjax-header-nav-list">
        <li><a id="s-comment" class="fas fa-comments fa-fw" target="_self" href='javascript:void(0)'></a></li>
        
          <li><a id="s-toc" class="s-toc fas fa-list fa-fw" target="_self" href='javascript:void(0)'></a></li>
        
      </ul>
    </div>
		<div class="nav-main">
      
        
        <a class="title flat-box" target="_self" href='/'>
          
          
            <i class='fas fa-basketball-ball'></i>
          
          
            Alan
          
        </a>
      

			<div class='menu navigation'>
				<ul class='nav-list-h m-pc'>
          
          
          
            
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover" href=/categories/
                  
                  
                  
                    id="categories"
                  >
                  <i class='fas fa-folder-open fa-fw'></i>分类
                </a>
                
              </li>
            
          
          
            
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover" href=/archives/
                  
                  
                  
                    id="archives"
                  >
                  <i class='fas fa-archive fa-fw'></i>归档
                </a>
                
              </li>
            
          
          
            
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover" href=/friends/
                  
                  
                  
                    id="friends"
                  >
                  <i class='fas fa-link fa-fw'></i>友链
                </a>
                
              </li>
            
          
          
            
            
              <li>
                <a class="menuitem flat-box header toggle-mode-btn">
                  <i class='fas fa-moon fa-fw'></i>dark
                </a>
              <li>
            
          
          
            
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover" 
                  
                  
                  >
                  <i class='fas fa-ellipsis-v fa-fw'></i>更多
                </a>
                
                  <ul class="list-v">
                    
                      
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover" href=https://alanwang.avosapps.us/
                  
                  
                    target="_blank"
                  
                  
                    id="https:alanwangavosappsus"
                  >
                  <i class='fas fa-comments fa-fw'></i>评论管理
                </a>
                
              </li>
            
          
                    
                  </ul>
                
              </li>
            
          
          
				</ul>
			</div>

      <div class="m_search">
        <form name="searchform" class="form u-search-form">
          <i class="icon fas fa-search fa-fw"></i>
          <input type="text" class="input u-search-input" placeholder="Search..." />
        </form>
      </div>

			<ul class='switcher nav-list-h m-phone'>
				
					<li><a class="s-search fas fa-search fa-fw" target="_self" href='javascript:void(0)'></a></li>
				
				<li>
          <a class="s-menu fas fa-bars fa-fw" target="_self" href='javascript:void(0)'></a>
          <ul class="menu-phone list-v navigation white-box">
            
              
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover" href=/categories/
                  
                  
                  
                    id="categories"
                  >
                  <i class='fas fa-folder-open fa-fw'></i>分类
                </a>
                
              </li>
            
          
            
              
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover" href=/archives/
                  
                  
                  
                    id="archives"
                  >
                  <i class='fas fa-archive fa-fw'></i>归档
                </a>
                
              </li>
            
          
            
              
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover" href=/friends/
                  
                  
                  
                    id="friends"
                  >
                  <i class='fas fa-link fa-fw'></i>友链
                </a>
                
              </li>
            
          
            
              
            
              <li>
                <a class="menuitem flat-box header toggle-mode-btn">
                  <i class='fas fa-moon fa-fw'></i>dark
                </a>
              <li>
            
          
            
              
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover" 
                  
                  
                  >
                  <i class='fas fa-ellipsis-v fa-fw'></i>更多
                </a>
                
                  <ul class="list-v">
                    
                      
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover" href=https://alanwang.avosapps.us/
                  
                  
                    target="_blank"
                  
                  
                    id="https:alanwangavosappsus"
                  >
                  <i class='fas fa-comments fa-fw'></i>评论管理
                </a>
                
              </li>
            
          
                    
                  </ul>
                
              </li>
            
          
            
          </ul>
        </li>
			</ul>
		</div>
	</div>
  </div>
</header>

  <div id="l_body">
    <div id="l_cover">
  
    
        <div id="half" class='cover-wrapper post focus' style="display: ;">
          
            <div id='cover-backstretch'></div>
          
          <div class='cover-body'>
  <div class='top'>
    
    
      <p class="title">Alan</p>
    
    
      <p class="subtitle">此刻想举重若轻，之前必要负重前行</p>
    
  </div>
  <div class='bottom'>
    <div class='menu navigation'>
      <div class='list-h'>
        
          
            <a href="https://alannum3.gitee.io/"
              
              
              id="https:alannum3giteeio">
              <img src='https://cdn.jsdelivr.net/gh/twitter/twemoji@13.0/assets/svg/1f389.svg'><p>Next.js博客</p>
            </a>
          
        
      </div>
    </div>
  </div>
</div>

          <div id="scroll-down" style="display: none;"><i class="fa fa-chevron-down scroll-down-effects"></i></div>
        </div>
    
  
  </div>

    <div id='safearea'>
      <div class='body-wrapper' id="pjax-container">
        

<div class='l_main'>
  <article class="article post white-box reveal md shadow article-type-post" id="post" itemscope itemprop="blogPost">
  


  
  <div class="article-meta" id="top">
    
    
    
      <h1 class="title">
        前端面试题
      </h1>
      <div class='new-meta-box'>
        
          
            
  <div class='new-meta-item category'>
    <a class='notlink'>
      <i class="fas fa-folder-open fa-fw" aria-hidden="true"></i>
      <a class="category-link" href="/categories/%E5%89%8D%E7%AB%AF/">前端</a><span class="sep"></span><a class="category-link" href="/categories/%E5%89%8D%E7%AB%AF/%E9%9D%A2%E8%AF%95%E9%80%A0%E8%BD%AE/">面试造轮</a>
    </a>
  </div>


          
        
          
            <div class="new-meta-item date" itemprop="dateUpdated" datetime="2020-07-24T15:03:00+08:00">
  <a class='notlink'>
    <i class="fas fa-edit fa-fw" aria-hidden="true"></i>
    <p>更新于：2020年7月24日 15:03</p>
  </a>
</div>

          
        
          
            
  <div class="new-meta-item wordcount">
    <a class='notlink'>
      <i class="fas fa-keyboard fa-fw" aria-hidden="true"></i>
      <p>字数：5.1k字</p>
    </a>
  </div>
  <div class="new-meta-item readtime">
    <a class='notlink'>
      <i class="fas fa-hourglass-half fa-fw" aria-hidden="true"></i>
      <p>时长：22分钟</p>
    </a>
  </div>


          
        
          
            
  <div class="new-meta-item browse leancloud">
    <a class='notlink'>
      
      <div id="lc-pv" data-title="前端面试题" data-path="/posts/20802113/">
        <i class="fas fa-fire fa-fw" aria-hidden="true"></i>
        <span id='number'><i class="fas fa-circle-notch fa-spin fa-fw" aria-hidden="true"></i></span>
        次浏览
      </div>
    </a>
  </div>


          
        
      </div>
    
  </div>


  
  <p>记录了自己疫情期间自学+复习总结的一些面试题+面试遇到的一些问题，文章将会持续更新。</p>
<a id="more"></a>

<h2 id="背景"><a href="#背景" class="headerlink" title="背景"></a>背景</h2><p><img src="" data-original="http://img.adoutu.com/picture/1538925790667.jpg" alt=""></p>
<p>由于今年疫情的原因以及一些个人原因，辞去了之前的实习工作。在家待了也快5个月了(期间经历了毕业设计+毕业答辩（2个月），算下来已经有3个月没有上班了😭)。去年在公司实习错过了秋招，今年又碰上疫情，对我这个应届生来说简直是难上加难。在家这段时间自己学习了React、webpack。用React搭建了一直想做但是耽搁了半年的博客，搭建出来后考虑后期维护成本（服务器费用），就没有继续维护了。改用了Hexo+github搭建了一个静态博客。真香😂。</p>
<h2 id="JS"><a href="#JS" class="headerlink" title="JS"></a>JS</h2><h3 id="原型及继承"><a href="#原型及继承" class="headerlink" title="原型及继承"></a>原型及继承</h3><p><img src="" data-original="https://gitee.com/alannum3/MyImages/raw/master/img/image-20200724160959828.png" alt="红宝书中的图"></p>
<p><strong>组合继承</strong></p>
<pre><code class="js">function Person(name) {
  this.name = name;
  this.features = [&#39;eyes&#39;];
}

function Student(name, id) {
  Person.call(this, name); //继承属性相当于super(this)
  this.id = id;
}

Student.prototype = new Person();

Student.prototype.sayHello = function () {
  console.log(&#39;hello&#39;);
};

const s1 = new Student(&#39;Alan&#39;, &#39;001&#39;);
const s2 = new Student(&#39;Bob&#39;, &#39;002&#39;);

console.log(s1 instanceof Person); // true
// 引用类型在各实例中不会相互影响
s1.features.push(&#39;hand&#39;);
console.log(s2.features); // [&quot;eyes&quot;]</code></pre>
<p>更多继承方式及其优缺点可以查看红宝书第六章（讲得非常“干”）</p>
<h3 id="深拷贝和浅拷贝"><a href="#深拷贝和浅拷贝" class="headerlink" title="深拷贝和浅拷贝"></a>深拷贝和浅拷贝</h3><p>在JS中，变量分为了基本类型和引用类型。对基本类型进行赋值时是对值进行拷贝的，而对引用类型进行赋值则是对地址进行拷贝。</p>
<div class="tabs" id="1"><ul class="nav-tabs"><li class="tab active"><a class="#1-1">基本类型</a></li><li class="tab"><a class="#1-2">引用类型</a></li></ul><div class="tab-content"><div class="tab-pane active" id="1-1"><pre><code class="js"><span class="keyword">let</span> a = <span class="number">1</span>;
<span class="keyword">let</span> b= a;
<span class="built_in">console</span>.log(a); <span class="comment">// 1</span>
a++;
<span class="built_in">console</span>.log(a); <span class="comment">// 2</span>
<span class="built_in">console</span>.log(b); <span class="comment">// 1</span></code></pre></div><div class="tab-pane" id="1-2"><pre><code class="js"><span class="keyword">const</span> obj = {
    name: <span class="string">'Alan'</span>,
    age: <span class="number">22</span>,
}
<span class="keyword">const</span> cloneObj = obj;
obj.age = <span class="number">18</span>;
<span class="built_in">console</span>.log(cloneObj.age); <span class="comment">// 18</span></code></pre></div></div></div>

<p>通过上面的例子我们知道，cloneObj和obj是指向同一个地址的，任何一方修改都会影响到对方，那如何创建一个独立的cloneObj?，这就要使用深拷贝和浅拷贝了。</p>
<p>深拷贝和浅拷贝的区别：</p>
<p>根据拷贝的层级进行区分，浅拷贝只进行一层拷贝，深拷贝进行多层拷贝。</p>
<ul>
<li>浅拷贝</li>
</ul>
<div class="tabs" id="2"><ul class="nav-tabs"><li class="tab active"><a class="#2-1">方法1</a></li><li class="tab"><a class="#2-2">方法2</a></li></ul><div class="tab-content"><div class="tab-pane active" id="2-1"><pre><code class="js"><span class="keyword">const</span> obj = {<span class="attr">a</span>:<span class="number">1</span>, <span class="attr">b</span>:{<span class="attr">b1</span>:<span class="number">1</span>, <span class="attr">b2</span>:<span class="number">2</span>}, <span class="attr">c</span>:<span class="number">0</span>};
<span class="function"><span class="keyword">function</span> <span class="title">shallowClone</span>(<span class="params">source</span>) </span>{
    <span class="keyword">const</span> result= {};
    <span class="keyword">for</span> (<span class="keyword">const</span> key <span class="keyword">in</span> source) {
        <span class="keyword">if</span> (source.hasOwnProperty(key)) {
            result[key] = source[key];
        }
    }
    <span class="keyword">return</span> result;
}
<span class="keyword">const</span> shallowObj = shallowClone(obj);
obj.a = <span class="number">10</span>;
<span class="built_in">console</span>.log(shallowObj.a); <span class="comment">// 1</span>
obj.b.b1 = <span class="number">6</span>;
<span class="built_in">console</span>.log(shallowObj.b.b1); <span class="comment">// 6</span></code></pre></div><div class="tab-pane" id="2-2"><pre><code class="js"><span class="keyword">const</span> obj = {<span class="attr">a</span>:<span class="number">1</span>, <span class="attr">b</span>:{<span class="attr">b1</span>:<span class="number">1</span>, <span class="attr">b2</span>:<span class="number">2</span>}, <span class="attr">c</span>:<span class="number">0</span>};
<span class="function"><span class="keyword">function</span> <span class="title">shallowClone1</span>(<span class="params">source</span>) </span>{
    <span class="keyword">return</span> <span class="built_in">Object</span>.assign({}, source);
}
<span class="keyword">const</span> shallowObj = shallowClone1(obj);
obj.a = <span class="number">10</span>;
<span class="built_in">console</span>.log(shallowObj.a); <span class="comment">// 1</span>
obj.b.b1 = <span class="number">6</span>;
<span class="built_in">console</span>.log(shallowObj.b.b1); <span class="comment">// 6</span></code></pre></div></div></div>

<ul>
<li>深拷贝</li>
</ul>
<div class="tabs" id="3"><ul class="nav-tabs"><li class="tab active"><a class="#3-1">方法1</a></li><li class="tab"><a class="#3-2">方法2</a></li></ul><div class="tab-content"><div class="tab-pane active" id="3-1"><pre><code class="js"><span class="keyword">const</span> obj = {<span class="attr">a</span>:<span class="number">1</span>, <span class="attr">b</span>:{<span class="attr">b1</span>:<span class="number">1</span>, <span class="attr">b2</span>:<span class="number">2</span>}, <span class="attr">c</span>:<span class="number">0</span>};
<span class="function"><span class="keyword">function</span> <span class="title">deepClone</span>(<span class="params">source</span>) </span>{
    <span class="keyword">const</span> result = {};
    <span class="keyword">for</span> (<span class="keyword">const</span> key <span class="keyword">in</span> source) {
        <span class="keyword">if</span> (source.hasOwnProperty(key)) {
            <span class="keyword">if</span> (<span class="keyword">typeof</span> source[key] === <span class="string">'object'</span>) {
                result[key] = deepClone(source[key]);
            } <span class="keyword">else</span> {
                result[key] = source[key];
            }
        }
    }
    <span class="keyword">return</span> result;
}
<span class="keyword">const</span> deepObj = deepClone(obj);
obj.a = <span class="number">10</span>;
<span class="built_in">console</span>.log(deepObj.a); <span class="comment">// 1</span>
obj.b.b1 = <span class="number">6</span>;
<span class="built_in">console</span>.log(deepObj.b.b1); <span class="comment">// 1</span></code></pre></div><div class="tab-pane" id="3-2"><pre><code class="js"><span class="keyword">const</span> obj = {<span class="attr">a</span>:<span class="number">1</span>, <span class="attr">b</span>:{<span class="attr">b1</span>:<span class="number">1</span>, <span class="attr">b2</span>:<span class="number">2</span>}, <span class="attr">c</span>:<span class="number">0</span>};
<span class="function"><span class="keyword">function</span> <span class="title">deepClone</span>(<span class="params">source</span>) </span>{
    <span class="keyword">return</span> <span class="built_in">JSON</span>.parse(<span class="built_in">JSON</span>.stringify(source));
}
<span class="keyword">const</span> deepObj = deepClone(obj);
obj.a = <span class="number">10</span>;
<span class="built_in">console</span>.log(deepObj.a); <span class="comment">// 1</span>
obj.b.b1 = <span class="number">6</span>;
<span class="built_in">console</span>.log(deepObj.b.b1); <span class="comment">// 1</span></code></pre></div></div></div>



<h3 id="数组去重"><a href="#数组去重" class="headerlink" title="数组去重"></a>数组去重</h3><div class="tabs" id="4"><ul class="nav-tabs"><li class="tab active"><a class="#4-1">方法1</a></li><li class="tab"><a class="#4-2">方法2</a></li><li class="tab"><a class="#4-3">方法3 Set</a></li></ul><div class="tab-content"><div class="tab-pane active" id="4-1"><pre><code class="js"><span class="comment">// 笨方法</span>
<span class="keyword">const</span> arr = [<span class="number">1</span>,<span class="number">3</span>,<span class="number">45</span>,<span class="number">6</span>,<span class="number">3</span>,<span class="number">2</span>,<span class="number">0</span>];
<span class="keyword">const</span> newArr = [];
<span class="keyword">let</span> isRepeat;
<span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; arr.length; i++) {
    isRepeat = <span class="literal">false</span>;
    <span class="keyword">for</span> (<span class="keyword">let</span> j = <span class="number">0</span>; j &lt; newArr.length; j++) {
        <span class="keyword">if</span> (arr[i] === newArr[j]) {
            isRepeat = <span class="literal">true</span>;
            <span class="keyword">break</span>;
        }
    }
    <span class="keyword">if</span> (!isRepeat) {
        newArr.push(arr[i]);
    }
}
<span class="built_in">console</span>.log(arr); <span class="comment">// [1, 3, 45, 6, 3, 2, 0]</span>
<span class="built_in">console</span>.log(newArr); <span class="comment">// [1, 3, 45, 6, 2, 0]</span></code></pre></div><div class="tab-pane" id="4-2"><pre><code class="js"><span class="keyword">const</span> arr = [<span class="number">1</span>,<span class="number">3</span>,<span class="number">45</span>,<span class="number">6</span>,<span class="number">3</span>,<span class="number">2</span>,<span class="number">0</span>];
<span class="keyword">const</span> newArr = arr.filter(<span class="function">(<span class="params">item,index</span>) =&gt;</span> {
    <span class="keyword">return</span> arr.indexOf(item) === index
})
<span class="built_in">console</span>.log(arr); <span class="comment">// [1, 3, 45, 6, 3, 2, 0]</span>
<span class="built_in">console</span>.log(newArr); <span class="comment">// [1, 3, 45, 6, 2, 0]</span></code></pre></div><div class="tab-pane" id="4-3"><pre><code class="js"><span class="keyword">const</span> arr = [<span class="number">1</span>,<span class="number">3</span>,<span class="number">45</span>,<span class="number">6</span>,<span class="number">3</span>,<span class="number">2</span>,<span class="number">0</span>];
<span class="keyword">const</span> newArr = [...new <span class="built_in">Set</span>(arr)];
<span class="built_in">console</span>.log(arr); <span class="comment">// [1, 3, 45, 6, 3, 2, 0]</span>
<span class="built_in">console</span>.log(newArr); <span class="comment">// [1, 3, 45, 6, 2, 0]</span></code></pre></div></div></div>



<h3 id="for-in-和-for-of"><a href="#for-in-和-for-of" class="headerlink" title="for in 和 for of"></a>for in 和 for of</h3><ul>
<li>for in 会遍历数组中的可枚举属性，包括原型。可以遍历对象，遍历的是key值</li>
<li>for of只是遍历数组的元素或者可以迭代的对象，不包括原型。遍历的是value值。</li>
</ul>
<pre><code class="js">Array.prototype.testMethod = function () {
    console.log(&#39;testMethod&#39;);
}
const mArr = [1,2,3,7];
const mObject = {
    name: &#39;Alan&#39;,
    age: 1
}
for (const key in mArr) {
    console.log(mArr[key]);
    // 1 2 3 7 
    /*       ƒ () {
        console.log(&#39;testMethod&#39;);
      } */
}
// 解决方案
for (const key in mArr) {
    if (mArr.hasOwnProperty(key)) {
        console.log(mArr[key]);
        // 1 2 3 7
    }
}
for (const key in mObject) {
    console.log(key);
    // name
    // age
}
try {
    for (const iterator of mObject) {
        console.log(iterator);
    }
} catch (error) {
    console.log(error);
    //mObject is not iterable
}</code></pre>
<h3 id="事件委托"><a href="#事件委托" class="headerlink" title="事件委托"></a>事件委托</h3><p>事件委托利用了事件冒泡，只指定了一个事件处理程序，就可以管理某一类型的所有事件。</p>
<p>例如，click事件会一直冒泡到document层次。例如下面例子中我们无需对所有li元素添加onclick事件，只需使用事件冒泡的特性来实现事件委托。</p>
<pre><code class="html">&lt;body&gt;
  &lt;ul id=&quot;myList&quot;&gt;
    &lt;li id=&quot;sayName&quot;&gt;Name&lt;/li&gt;
    &lt;li id=&quot;sayHello&quot;&gt;Hello&lt;/li&gt;
    &lt;li id=&quot;sayAge&quot;&gt;Age&lt;/li&gt;
  &lt;/ul&gt;
  &lt;script&gt;    
    const myList = document.getElementById(&#39;myList&#39;);
    myList.addEventListener(&#39;click&#39;, function (e) {
      const target = e.target;
      if (target.id === &#39;sayName&#39;) {
        alert(&#39;Alan&#39;);
      } else if(target.id === &#39;sayHello&#39;) {
        alert(&#39;Hello&#39;);
      } else {
        alert(&#39;sayAge&#39;);
      }
    })
  &lt;/script&gt;
&lt;/body&gt;</code></pre>
<h3 id="实现滑动动画"><a href="#实现滑动动画" class="headerlink" title="实现滑动动画"></a>实现滑动动画</h3><pre><code class="html">&lt;div id=&quot;myDiv&quot;&gt;&lt;/div&gt;

&lt;script&gt;
    const myDiv = document.getElementById(&#39;myDiv&#39;);
    const time = Date.now(); //时间戳

    const transition = setInterval(() =&gt; {
    const timeLength = Date.now() - time;
    const step = (5000 - timeLength) / 1000;
    console.log(step);

    if (timeLength &gt; 5000) {
        clearInterval(transition);
        // 5s后结束
    }
    myDiv.style.left = myDiv.offsetLeft + step + &#39;px&#39;;
}, 50);
&lt;/script&gt;</code></pre>
<h3 id="数组扁平化"><a href="#数组扁平化" class="headerlink" title="数组扁平化"></a>数组扁平化</h3><div class="tabs" id="5"><ul class="nav-tabs"><li class="tab active"><a class="#5-1">方法1</a></li><li class="tab"><a class="#5-2">方法2</a></li><li class="tab"><a class="#5-3">方法3</a></li><li class="tab"><a class="#5-4">方法4</a></li></ul><div class="tab-content"><div class="tab-pane active" id="5-1"><pre><code class="js"><span class="comment">// 笨方法：遍历</span>
<span class="keyword">const</span> arr = [<span class="number">1</span>,<span class="number">2</span>,<span class="number">7</span>,[<span class="number">2</span>,[<span class="number">2</span>,<span class="number">3</span>],<span class="number">6</span>]];
<span class="function"><span class="keyword">function</span> <span class="title">flat</span>(<span class="params">arr</span>) </span>{
    <span class="keyword">let</span> result = [];
    <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; arr.length; i++) {
        <span class="keyword">if</span>(<span class="built_in">Array</span>.isArray(arr[i])) {
            result = result.concat(flat(arr[i]));
        } <span class="keyword">else</span> {
            result.push(arr[i]);
        }
    }
    <span class="keyword">return</span> result;
}
<span class="built_in">console</span>.log(flat(arr));</code></pre></div><div class="tab-pane" id="5-2"><pre><code class="js"><span class="comment">// 使用for of</span>
<span class="keyword">let</span> arr = [<span class="number">1</span>,<span class="number">2</span>,<span class="number">7</span>,[<span class="number">2</span>,[<span class="number">2</span>,<span class="number">3</span>],<span class="number">6</span>]];
<span class="function"><span class="keyword">function</span> <span class="title">flat</span>(<span class="params">arr</span>) </span>{
    <span class="keyword">let</span> newArr =[];
    <span class="keyword">for</span> (<span class="keyword">const</span> item <span class="keyword">of</span> arr) {
        <span class="keyword">if</span>(<span class="built_in">Array</span>.isArray(item)){
            newArr = newArr.concat(flat(item));
        } <span class="keyword">else</span> {
            newArr.push(item);
        }
    }
    <span class="keyword">return</span> newArr;
}
<span class="built_in">console</span>.log(flat(arr));</code></pre></div><div class="tab-pane" id="5-3"><pre><code class="js"><span class="keyword">const</span> arr = [<span class="number">1</span>,<span class="number">2</span>,<span class="number">7</span>,[<span class="number">2</span>,[<span class="number">2</span>,<span class="number">3</span>],<span class="number">6</span>]];
<span class="function"><span class="keyword">function</span> <span class="title">flat</span>(<span class="params">arr</span>) </span>{
    <span class="keyword">while</span> (arr.some(<span class="function"><span class="params">item</span> =&gt;</span> <span class="built_in">Array</span>.isArray(item))) {
        arr = [].concat(...arr);
    }
    <span class="keyword">return</span> arr;
}
<span class="built_in">console</span>.log(flat(arr));</code></pre></div><div class="tab-pane" id="5-4"><pre><code class="js"><span class="comment">// 骚操作</span>
<span class="keyword">const</span> arr = [<span class="number">1</span>,<span class="number">2</span>,<span class="number">7</span>,[<span class="number">2</span>,[<span class="number">2</span>,<span class="number">3</span>],<span class="number">6</span>]];
<span class="function"><span class="keyword">function</span> <span class="title">flat</span>(<span class="params">arr</span>) </span>{
    <span class="keyword">const</span> result = arr.toString().split(<span class="string">','</span>).map(<span class="function"><span class="params">item</span> =&gt;</span> {
        <span class="keyword">return</span> +item;
    });
    <span class="keyword">return</span> result;
}
<span class="built_in">console</span>.log(flat(arr));</code></pre></div></div></div>



<h3 id="eventLoop"><a href="#eventLoop" class="headerlink" title="eventLoop"></a>eventLoop</h3><p>JS是单线程的，那为什么不是多线程的呢，设想一个场景，一个线程修改了body的background为red，另一个线程修改了body的background为green。那最终浏览器就不知道background到底为什么。由此可以看到多线程会为浏览器的DOM操作带来很多同步问题。<a href="http://www.ruanyifeng.com/blog/2014/10/event-loop.html" target="_blank" rel="noopener">参考资料</a></p>
<p><strong>JS的任务可以分为同步任务和异步任务。</strong></p>
<p>1.同步任务优先在主线程上执行，会形成一个执行栈。</p>
<p>2.异步任务会被放入任务<strong>队列</strong>中，当执行栈清空时会读取任务队列中的任务丢进执行栈中。</p>
<p>1、2两步反复执行形成了eventLoop。</p>
<p>如果将任务细分的话还可以分成<strong>宏任务和微任务</strong>：</p>
<ul>
<li>macro-task(宏任务)：包括整体代码script，setTimeout，setInterval</li>
<li>micro-task(微任务)：Promise.then()，process.nextTick</li>
</ul>
<p>优先级：process.nextTick&gt;Promise.then()</p>
<p><strong>当执行栈中没有任务时，微任务总是优先于宏任务执行</strong></p>
<p>详细查看文章<a href="https://juejin.im/post/59e85eebf265da430d571f89" target="_blank" rel="noopener">这一次，彻底弄懂 JavaScript 执行机制</a></p>
<h3 id="this"><a href="#this" class="headerlink" title="this"></a>this</h3><ul>
<li>指向<strong>调用</strong>它的那个对象，函数运行时获得的。</li>
<li>箭头函数的this指向取决于<strong>定义</strong>时最近一层的非箭头函数的this值。取决于外部的上下文。</li>
</ul>
<h3 id="闭包"><a href="#闭包" class="headerlink" title="闭包"></a>闭包</h3><ul>
<li>当前函数的执行上下文中的内容被该上下文以外的内容<strong>占用</strong>，导致当前上下文无法释放。</li>
<li>闭包是指有权访问另一个函数作用域中的变量的函数。</li>
<li>创建闭包的常见方式，就是在一个函数内部创建另一个函数</li>
</ul>
<p><strong>涉及到函数作用域链，词法作用域，后期有空再补充</strong></p>
<h3 id="求数组最大值"><a href="#求数组最大值" class="headerlink" title="求数组最大值"></a>求数组最大值</h3><pre><code class="js">const arr = [1,2,1,4,2,10];
console.log(Math.max.apply(null, arr));</code></pre>
<pre><code class="js">const arr = [1,2,1,4,2,10];
console.log(arr.sort((a,b) =&gt; (a-b))[arr.length-1]);</code></pre>
<pre><code class="js">const arr = [1,2,1,4,2,10];
console.log(Math.max(...arr));</code></pre>
<h3 id="Promise"><a href="#Promise" class="headerlink" title="Promise"></a>Promise</h3><pre><code class="js">// new Promise(executor)，当new Promise被创建，executor自动执行
let promise = new Promise(function (resolve, reject) {
    resolve(&#39;finished&#39;);
    // reject(new Error);
});
// Promise.then(f1,f2) f1在resolve后运行（参数为resolve结果），f2在reject后运行（参数为reject错误）
promise.then(
    result =&gt; console.log(result), //finished
    error =&gt; console.log(error) //输出错误
);</code></pre>
<ul>
<li>promise.all()同时执行多个promise，只要有一个promise被reject，那么将不再执行</li>
<li>promise.allSettled()和promise.all()类似，只是会等所有promise执行完。</li>
<li>promise.race()返回最快执行完的promise结果。</li>
</ul>
<p><strong>实现一个sleep</strong></p>
<pre><code class="js">function sleep(ms) {
  return new Promise((resolve) =&gt; {
    setTimeout(resolve, ms);
  });
}

(async () =&gt; {
  console.log(&#39;2s后输出内容...&#39;);
  await sleep(2000);
  console.log(&#39;666&#39;);
})();</code></pre>
<div class="note link"><p><a href="https://zh.javascript.info/promise-basics" target="_blank" rel="noopener">https://zh.javascript.info/promise-basics</a></p></div>

<h3 id="防抖和节流"><a href="#防抖和节流" class="headerlink" title="防抖和节流"></a>防抖和节流</h3><ul>
<li>节流</li>
</ul>
<p>函数在一定时间内只执行一次，比如点击按钮后回去服务器获取数据，使用节流可以防止短时间内请求多次，减少服务器的压力</p>
<ul>
<li>防抖</li>
</ul>
<p>在一定时间后才执行（触发多次只会执行一次）。应用场景：input搜索框在wait秒后再发送请求</p>
<pre><code class="html">&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&quot;&gt;
&lt;head&gt;
  &lt;meta charset=&quot;UTF-8&quot;&gt;
  &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&gt;
  &lt;title&gt;Document&lt;/title&gt;
  &lt;style&gt;
    body {
      height: 4000px;
      width: 100px;
      background: rgb(241, 165, 165);
    }
  &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
  &lt;button id=&quot;fetchBtn&quot;&gt;fetch data&lt;/button&gt;
  &lt;span&gt;searchBar&lt;/span&gt;&lt;input id=&quot;inputBar&quot;/&gt;
  &lt;script&gt;
    // 节流
    function throttle(func, wait) {
      let last = 0;
      return function (...args) {
        let now = new Date();
        if (now - last &gt; wait) {
          last = now;
          func.apply(this, args);
        }
      }
    }

    // 防抖
    function debounce(func, wait) {
      let timer = 0;
      return function (...args) {
        if (timer) clearTimeout(timer);
        timer = setTimeout(() =&gt; {
          func.apply(this, args);
        }, wait)
      }
    }

    document.getElementById(&#39;fetchBtn&#39;).addEventListener(&#39;click&#39;, throttle(function (numb) {
      console.log(&#39;fetching&#39;);
    },1000));
    document.getElementById(&#39;inputBar&#39;).addEventListener(&#39;input&#39;, debounce(function (numb) {
      console.log(&#39;searching&#39;);
    },1000));
  &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<h3 id="XMLHttpRequest"><a href="#XMLHttpRequest" class="headerlink" title="XMLHttpRequest"></a>XMLHttpRequest</h3><p>常用方法：</p>
<ul>
<li>open(method, url, [async, user, password])初始化，async(false/true)控制同步/异步</li>
<li>send([body])发送请求</li>
<li>abort()中止请求</li>
<li>setRequestHeader(name, value)设置请求头</li>
</ul>
<p>常用属性：</p>
<p>status: 404/200…</p>
<p>statusText: Not Found/OK…</p>
<p>responseType: 响应格式</p>
<p>readyState： 状态</p>
<p>withCredentials: 跨域设置</p>
<pre><code class="javascript">UNSENT = 0; // 初始状态
OPENED = 1; // open 被调用
HEADERS_RECEIVED = 2; // 接收到 response header
LOADING = 3; // 响应正在被加载（接收到一个数据包）
DONE = 4; // 请求完成</code></pre>
<p>常用监听事件：</p>
<ul>
<li>onload</li>
<li>onerror</li>
<li>onprogress</li>
</ul>
<h3 id="题目"><a href="#题目" class="headerlink" title="题目"></a>题目</h3><pre><code class="js">const a = [&#39;1&#39;, &#39;2&#39;, &#39;3&#39;].map(parseInt);
// 数组a中的&#39;1&#39;转化为10进制。
console.log(a); // [1, NaN, NaN]
// map的三个参数(item,index,array)
/* parseInt(string, radix)
当radix等于0或者undefined或者没有指定时，如果string以&#39;0x&#39;或者&#39;&#39;0X&#39;开头，则radix=16
以&#39;0&#39;开头，根据实际情况radix=10/8。 */</code></pre>
<pre><code class="js">const users = [
    {
        name: &#39;Alan&#39;,
        age: 19
    },
    {
        name: &#39;Bob&#39;,
        age: 25
    }
];
const userList = users.sort((a, b) =&gt; b.age-a.age);
// 根据年龄进行排序，注意：sort会改变原来的数组</code></pre>
<pre><code class="js">function isSameLetter(a, b) {
    a = a.toString().toLowerCase();
    b = b.toString().toLowerCase();
    return a.split(&#39;&#39;).sort().join(&#39;&#39;) === b.split(&#39;&#39;).sort().join(&#39;&#39;);
}
console.log(isSameLetter(&#39;176as&#39;, &#39;a17s6&#39;));
//判断两者是否是由相同的字母组成，顺序可以不一样</code></pre>
<h2 id="CSS"><a href="#CSS" class="headerlink" title="CSS"></a>CSS</h2><h3 id="css单位"><a href="#css单位" class="headerlink" title="css单位"></a>css单位</h3><ul>
<li><p>rem：相对于根元素的字体大小（html）css3</p>
<p>如果html的font-size为16px（默认），那么1rem=16px</p>
</li>
<li><p>em：如果该元素有设置font-size，那么相对于该元素。如果没有设置则相对于父元素。</p>
<p>例如，div设置了font-size为10px，那么该div中使用em时，1em为10px</p>
<p>如果该元素没有设置font-size且父元素设置font-size为20px，那么1em为20px</p>
</li>
<li><p>vh/vw相对于视窗，10vh=1/10的屏幕高</p>
</li>
</ul>
<h3 id="translate和使用绝对定位的区别"><a href="#translate和使用绝对定位的区别" class="headerlink" title="translate和使用绝对定位的区别"></a>translate和使用绝对定位的区别</h3><p>translate会占据原来的位置，绝对定位会脱离文档流。</p>
<h3 id="BFC"><a href="#BFC" class="headerlink" title="BFC"></a>BFC</h3><p>概念：很模糊抽象，是页面上一个隔离的独立容器，容器中的子元素不会影响到外面的元素。</p>
<p>试用场景：清理浮动，解决margin重叠</p>
<p><strong>一个元素不能同时存在两个BFC中</strong></p>
<p>创建方式：</p>
<ul>
<li>根元素</li>
<li>float不为none</li>
<li>position: absolute/fixed</li>
<li>display: inline-block/table-cell</li>
<li>overflow不为visible</li>
<li>弹性盒子（<code>display: flex/inline-flex</code>）</li>
</ul>
<h3 id="清理浮动"><a href="#清理浮动" class="headerlink" title="清理浮动"></a>清理浮动</h3><ul>
<li><p>伪类元素</p>
<pre><code class="css">.clearfix::after {
    content: &#39;&#39;;
    display: blcok;
    clear: both;
}</code></pre>
</li>
<li><p>创建BFC</p>
<pre><code class="css">overflow: hidden;

overflow: auto;</code></pre>
</li>
<li><p>空盒子</p>
<pre><code class="html">&lt;div style=&quot;clear: both;&quot;&gt;&lt;/div&gt;</code></pre>
</li>
</ul>
<h3 id="选择器优先级"><a href="#选择器优先级" class="headerlink" title="选择器优先级"></a>选择器优先级</h3><p><strong>!important &gt; 行内样式&gt;ID选择器 &gt; 类选择器 &gt; 标签 &gt; 通配符 &gt; 继承</strong></p>
<h3 id="垂直居中"><a href="#垂直居中" class="headerlink" title="垂直居中"></a>垂直居中</h3><pre><code class="html">&lt;div class=&quot;outer&quot;&gt;
    &lt;div class=&quot;inner&quot;&gt;&lt;/div&gt;
&lt;/div&gt;</code></pre>
<ul>
<li>table-cell</li>
</ul>
<pre><code class="css">.outer {
    width: 400px;
    height: 400px;
    background: aqua;
    display: table-cell;
    vertical-align: middle;
    text-align: center;
}
.inner {
    width: 100px;
    height: 100px;
    background: brown;
    display: inline-block;
}</code></pre>
<ul>
<li><p>flex布局</p>
</li>
<li><p>绝对定位</p>
<div class="tabs" id="6"><ul class="nav-tabs"><li class="tab active"><a class="#6-1">已知子元素宽高</a></li><li class="tab"><a class="#6-2">未知子元素宽高</a></li><li class="tab"><a class="#6-3">margin</a></li></ul><div class="tab-content"><div class="tab-pane active" id="6-1"><pre><code class="css"><span class="selector-class">.outer</span> {
    <span class="attribute">position</span>: relative;
}
<span class="selector-class">.inner</span> {
    <span class="attribute">position</span>: absolute;
    <span class="attribute">left</span>: <span class="number">50%</span>;
    <span class="attribute">top</span>: <span class="number">50%</span>;
    <span class="attribute">margin-left</span>: -<span class="number">50px</span>; <span class="comment">/* 宽度一半 */</span>
    <span class="attribute">margin-top</span>: -<span class="number">50px</span>; <span class="comment">/* 高度一半 */</span>
}</code></pre></div><div class="tab-pane" id="6-2"><pre><code class="css"><span class="selector-class">.outer</span> {
    <span class="attribute">position</span>: relative;
}
<span class="selector-class">.inner</span> {
    <span class="attribute">position</span>: absolute;
    <span class="attribute">left</span>: <span class="number">50%</span>;
    <span class="attribute">top</span>: <span class="number">50%</span>;
    <span class="attribute">transform</span>: <span class="built_in">translate</span>(-<span class="number">50%</span>, -<span class="number">50%</span>);
}</code></pre></div><div class="tab-pane" id="6-3"><pre><code class="css"><span class="selector-class">.outer</span> {
    <span class="attribute">position</span>: relative;
}
<span class="selector-class">.inner</span> {
    <span class="attribute">position</span>: absolute;
    <span class="attribute">left</span>: <span class="number">0</span>;
    <span class="attribute">right</span>: <span class="number">0</span>;
    <span class="attribute">top</span>: <span class="number">0</span>;
    <span class="attribute">bottom</span>: <span class="number">0</span>;
    <span class="attribute">margin</span>: auto;
}</code></pre></div></div></div>



</li>
</ul>
<h2 id="HTML"><a href="#HTML" class="headerlink" title="HTML"></a>HTML</h2><h3 id="meta标签"><a href="#meta标签" class="headerlink" title="meta标签"></a>meta标签</h3><pre><code>&lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&gt;</code></pre><h2 id="网络"><a href="#网络" class="headerlink" title="网络"></a>网络</h2><h3 id="从输入URL到页面加载发生了什么"><a href="#从输入URL到页面加载发生了什么" class="headerlink" title="从输入URL到页面加载发生了什么"></a>从输入URL到页面加载发生了什么</h3><ol>
<li>浏览器查找当前URL是否存在缓存，并比较缓存是否过期</li>
<li>根据DNS解析得到IP地址</li>
<li>建立TCP连接（3次握手）</li>
<li>HTTP发请求</li>
<li>服务器处理请求，返回数据</li>
<li>渲染页面，构建DOM树</li>
<li>关闭TCP连接（4次挥手）</li>
</ol>
<h3 id="常用状态码"><a href="#常用状态码" class="headerlink" title="常用状态码"></a>常用状态码</h3><table>
<thead>
<tr>
<th align="left">分类</th>
<th align="left">分类描述</th>
</tr>
</thead>
<tbody><tr>
<td align="left">1**</td>
<td align="left">信息，服务器收到请求，需要请求者继续执行操作</td>
</tr>
<tr>
<td align="left">2**</td>
<td align="left">成功，操作被成功接收并处理</td>
</tr>
<tr>
<td align="left">3**</td>
<td align="left">重定向，需要进一步的操作以完成请求</td>
</tr>
<tr>
<td align="left">4**</td>
<td align="left">客户端错误，请求包含语法错误或无法完成请求</td>
</tr>
<tr>
<td align="left">5**</td>
<td align="left">服务器错误，服务器在处理请求的过程中发生了错误</td>
</tr>
</tbody></table>
<table>
<thead>
<tr>
<th align="left">状态码</th>
<th>描述</th>
</tr>
</thead>
<tbody><tr>
<td align="left">200</td>
<td>请求成功</td>
</tr>
<tr>
<td align="left">204</td>
<td>无内容。服务器成功处理，但未返回内容</td>
</tr>
<tr>
<td align="left">301</td>
<td>永久移动。请求的资源已被永久的移动到新URI，返回信息会包括新的URI，浏览器会自动定向到新URI。今后任何新的请求都应使用新的URI代替</td>
</tr>
<tr>
<td align="left">302</td>
<td>临时移动。与301类似。但资源只是临时被移动。客户端应继续使用原有URI</td>
</tr>
<tr>
<td align="left">304</td>
<td>未修改。所请求的资源未修改，服务器返回此状态码时，不会返回任何资源。客户端通常会缓存访问过的资源，通过提供一个头信息指出客户端希望只返回在指定日期之后修改的资源</td>
</tr>
<tr>
<td align="left">400</td>
<td>客户端请求的语法错误，服务器无法理解</td>
</tr>
<tr>
<td align="left">401</td>
<td>请求要求用户的身份认证</td>
</tr>
<tr>
<td align="left">403</td>
<td>服务器理解请求客户端的请求，但是拒绝执行此请求</td>
</tr>
<tr>
<td align="left">404</td>
<td>服务器无法根据客户端的请求找到资源（网页）。通过此代码，网站设计人员可设置”您所请求的资源无法找到”的个性页面</td>
</tr>
<tr>
<td align="left">500</td>
<td>服务器内部错误，无法完成请求</td>
</tr>
</tbody></table>
<h3 id="网络七层协议"><a href="#网络七层协议" class="headerlink" title="网络七层协议"></a>网络七层协议</h3><p>引用网上的一张图，如侵权请联系我删除😂</p>
<p><img src="" data-original="https://gitee.com/alannum3/MyImages/raw/master/img/image-20200601083205320.png" alt="image-20200601083205320"></p>
<blockquote>
<p>引自维基百科<a href="https://zh.wikipedia.org/wiki/TCP/IP协议族" target="_blank" rel="noopener">TCP/IP协议族</a>是一个网络通信模型，以及一整个网络传输协议家族，为互联网的基础通信架构。该协议家族的两个核心协议：TCP（传输控制协议）和IP（网际协议），为该家族中最早通过的标准。这个协议族由互联网工程任务组负责维护。</p>
</blockquote>
<p><strong>TCP（Transmission Control Protocol 传输控制协议）:</strong>通过三次握手与服务器建立一个<code>全双工</code>的通信，在数据传送之前把数据分割成IP包，在到达服务器时再将他们重组。</p>
<p><strong>IP（Internet Protocol 网际协议）:</strong>IP负责客户端与服务端之间的通信，IP负责在因特网上发送和接受数据包。通过IP，数据被分割成小的独立的包，然后通过互联网在计算机之间传送。</p>
<h3 id="HTTP"><a href="#HTTP" class="headerlink" title="HTTP"></a>HTTP</h3><p>HTTP(HyperText Transfer Protocol)，基于TCP实现的应用层协议。HTTP是一个<strong>无状态</strong>的协议，即客户端和服务端之间不用建立持久的链接，当客户端发送一个请求，服务端返回响应时，连接就被关闭了。</p>
<p>当请求一个url时<code>www.baidu.com</code>，首先<code>DNS</code>解析出该地址对应的IP地址，然后将相关信息封装成一个HTTP请求数据包。然后TCP（三次握手）建立连接，连接成功后发送HTTP请求，服务端响应回来后，关闭TCP连接。如果浏览器或服务器在头部加入了<code>Connection:keep-alive</code>，那么TCP在发送完信息后仍然保存连接，这样可以减少TCP连接次数，能提升性能和服务器吞吐率。</p>
<p><strong>缺点：</strong></p>
<ul>
<li>通行使用明文，不安全</li>
<li>不验证身份，可能遭遇伪装</li>
<li>无法验证报文完整性，可能遭遇篡改</li>
</ul>
<p>前面提到了HTTP是一个无状态的协议，那如何让它拥有状态？</p>
<p>session和cookie</p>
<p><strong>cookie(key/value形式):</strong></p>
<p>客户端返回cookie附在响应头中的<code>Set-Cookie</code>或者<code>Set-Cookie2</code>，cookie保存在客户端，下次访问时连同cookie发送给服务端</p>
<p><strong>cookie的同源和跨域:</strong></p>
<p>cookie只关注域名，忽略协议和端口，意思是协议或者端口不同不属于跨域</p>
<p><strong>session机制（服务端维护的），基于cookie工作：</strong></p>
<p>session和sessionId（返回给客户端）</p>
<p>当客户端访问服务端时，首先检查请求中是否包括sessionId。如果有，通过sessionId检索出对应session进行一系列操作。如果没有sessionId，创建一个session以此对应的sessionId通过cookie存在客户端，客户端之后访问服务端时通过这个sessionId维护HTTP状态。</p>
<h3 id="HTTPS"><a href="#HTTPS" class="headerlink" title="HTTPS"></a>HTTPS</h3><p><code>HTTPS = HTTP + TLS/SSL</code></p>
<p>经过加密的HTTP，更加安全，利用<code>SSL/TLS</code>加密数据包；</p>
<p><code>TLS/SSL</code>:安全传输层协议Transport Layer Security</p>
<p><strong>TSL/SSL</strong>涉及到三种算法：</p>
<ul>
<li>散列函数Hash：MD5、SHA1、SHA256（函数单向不可逆，加密传输信息以及信息摘要）</li>
<li>对称加密：AES-CBC、DES、3DES、AES-GCM</li>
<li>非对称加密：RSA（分公钥和私钥）</li>
</ul>
<p><strong>TSL工作方式：</strong></p>
<p>首先使用<strong>非对称加密算法加密</strong>进行通信，服务端返回公钥并协商使用对称加密后的密钥对通信进行加密。</p>
<p><strong>RSA存在的隐患：</strong></p>
<p>无法确保服务器身份的合法性（因为公钥不包含服务器信息）：</p>
<p>例如：</p>
<p>C（客户端）和S（服务端）通信时，H（劫持者）截获了S与C的通信，H将自己的公钥pub_H给了C，当C向S发送请求时，实际上是使用pub_H加密的，这样H就能获取到C发送的消息了。这样原本属于C和S的通信现在就变成了C和H的通行了。</p>
<p><strong>解决方案：</strong>引入第三方认证机构，验证公钥拥有者的信息然后颁发证书，简称<code>PKI体系</code></p>
<pre><code>- S向第三方机构提交公钥和公司相关信息
- CA（即三方认证机构）通过一系列渠道验证审核S信息的正确性
- 审核通过后，CA向S签发证书，证书信息包括：S的公钥、S公司相关信息、签发机构CA的信息、有效时间、证书序列号（前面这些信息全是明文显示）。签名（使用散列函数计算公开的明文信息的信息摘要，然后用CA的私钥对信息摘要加密）
- C向S发送请求，S返回证书
- C读取证书中的明文信息，采用相同的散列函数计算得到信息摘要，然后利用CA对应的公钥对签名进行解密，对比证书中的信息摘要来验证证书/公钥的合法性。</code></pre><h3 id="跨域"><a href="#跨域" class="headerlink" title="跨域"></a>跨域</h3><p>非同源请求，均为跨域。名词解释：<em>同源 —— 如果两个页面拥有相同的协议（protocol），端口（port）和主机（host），那么这两个页面就属于同一个源（origin）。</em></p>
<p><img src="" data-original="https://gitee.com/alannum3/MyImages/raw/master/img/image-20200403163435103.png" alt="image-20200403163435103"></p>
<p><strong>jsonp:</strong></p>
<p>通过script标签的src属性来实现</p>
<p><strong>CORS:</strong></p>
<p>服务端设置Access-Control-Allow-Origin和Access-Control-Allow-Credentials为true</p>
<p>Vue设置跨域：</p>
<p>vue.http.options.credentials = true</p>
<p><strong>Nginx代理</strong></p>
<h2 id="框架"><a href="#框架" class="headerlink" title="框架"></a>框架</h2><h3 id="单页应用优缺点"><a href="#单页应用优缺点" class="headerlink" title="单页应用优缺点"></a>单页应用优缺点</h3><p>优点：</p>
<ul>
<li>基于ajax加载数据，无需刷新页面，用户体验好</li>
<li>前后端分离，后端代码可以应用到多端</li>
<li>减轻服务器压力</li>
</ul>
<p>缺点：</p>
<ul>
<li>不利于SEO，可以使用SSR解决</li>
<li>首屏加载速度慢</li>
<li>不支持浏览器前进后退功能</li>
</ul>

  
  
    
    <div class='footer'>
      
      
      
        <div class='copyright'>
          <blockquote>
            
              
                <p>博客内容遵循 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 协议</p>

              
            
              
                <p>本文永久链接是：<a href=https://alanwang.site/posts/20802113/>https://alanwang.site/posts/20802113/</a></p>
              
            
          </blockquote>
        </div>
      
      
    </div>
  
  
    


  <div class='article-meta' id="bottom">
    <div class='new-meta-box'>
      
        
          <div class="new-meta-item date" itemprop="dateUpdated" datetime="2020-07-24T15:03:00+08:00">
  <a class='notlink'>
    <i class="fas fa-edit fa-fw" aria-hidden="true"></i>
    <p>更新于：2020年7月24日 15:03</p>
  </a>
</div>

        
      
        
          
  
  <div class="new-meta-item meta-tags"><a class="tag" href="/tags/%E9%9D%A2%E8%AF%95%E9%A2%98/" rel="nofollow"><i class="fas fa-tags fa-fw" aria-hidden="true"></i><p>面试题</p></a></div>


        
      
        
          
  <div class="new-meta-item share -mob-share-list">
  <div class="-mob-share-list share-body">
    
      
        <a class="-mob-share-qq" title="" rel="external nofollow noopener noreferrer"
          
          href="http://connect.qq.com/widget/shareqq/index.html?url=https://alanwang.site/posts/20802113/&title=前端面试题 - Alan|前端博客&summary=记录了自己疫情期间自学+复习总结的一些面试题+面试遇到的一些问题，文章将会持续更新。"
          
          >
          
            <img src="" data-original="https://cdn.jsdelivr.net/gh/volantis-x/cdn-org/logo/128/qq.png">
          
        </a>
      
    
      
        <a class="-mob-share-qzone" title="" rel="external nofollow noopener noreferrer"
          
          href="https://sns.qzone.qq.com/cgi-bin/qzshare/cgi_qzshare_onekey?url=https://alanwang.site/posts/20802113/&title=前端面试题 - Alan|前端博客&summary=记录了自己疫情期间自学+复习总结的一些面试题+面试遇到的一些问题，文章将会持续更新。"
          
          >
          
            <img src="" data-original="https://cdn.jsdelivr.net/gh/volantis-x/cdn-org/logo/128/qzone.png">
          
        </a>
      
    
      
        <a class="-mob-share-weibo" title="" rel="external nofollow noopener noreferrer"
          
          href="http://service.weibo.com/share/share.php?url=https://alanwang.site/posts/20802113/&title=前端面试题 - Alan|前端博客&summary=记录了自己疫情期间自学+复习总结的一些面试题+面试遇到的一些问题，文章将会持续更新。"
          
          >
          
            <img src="" data-original="https://cdn.jsdelivr.net/gh/volantis-x/cdn-org/logo/128/weibo.png">
          
        </a>
      
    
      
    
      
    
  </div>
</div>



        
      
    </div>
  </div>


  
  

  
    <div class="prev-next">
      
        <a class='prev' href='/posts/b4c78541/'>
          <p class='title'><i class="fas fa-chevron-left" aria-hidden="true"></i>webpack4.0进阶（二）</p>
          <p class='content'>TS、Eslint、webpack性能优化


基础内容打包TS文件使用ts-loader
npm i ts-loader typescript -D项目目录如下：
ts-test
 ├── d...</p>
        </a>
      
      
        <a class='next' href='/posts/84d16999/'>
          <p class='title'>webpack4.0进阶（一）<i class="fas fa-chevron-right" aria-hidden="true"></i></p>
          <p class='content'>webpack4.0进阶学习


Tree Shaking按需打包文件index.jsmoment.jsimport { used } from './moment';

used();expo...</p>
        </a>
      
    </div>
  
</article>


  

  <article class="post white-box reveal shadow" id="comments">
    <p ct><i class='fas fa-comments'></i> 评论</p>
    
    <div id="valine_container" class="valine_thread">
  <i class="fas fa-cog fa-spin fa-fw fa-2x"></i>
</div>

  </article>






</div>
<aside class='l_side'>
  
  
    
    



  <section class="widget toc-wrapper shadow desktop mobile" id="toc-div" >
    
  <header>
    
      <i class="fas fa-list fa-fw" aria-hidden="true"></i><span class='name'>本文目录</span>
    
  </header>


    <div class='content'>
        <ol class="toc"><li class="toc-item toc-level-2"><a class="toc-link" href="#背景"><span class="toc-text">背景</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#JS"><span class="toc-text">JS</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#原型及继承"><span class="toc-text">原型及继承</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#深拷贝和浅拷贝"><span class="toc-text">深拷贝和浅拷贝</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#数组去重"><span class="toc-text">数组去重</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#for-in-和-for-of"><span class="toc-text">for in 和 for of</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#事件委托"><span class="toc-text">事件委托</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#实现滑动动画"><span class="toc-text">实现滑动动画</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#数组扁平化"><span class="toc-text">数组扁平化</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#eventLoop"><span class="toc-text">eventLoop</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#this"><span class="toc-text">this</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#闭包"><span class="toc-text">闭包</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#求数组最大值"><span class="toc-text">求数组最大值</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Promise"><span class="toc-text">Promise</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#防抖和节流"><span class="toc-text">防抖和节流</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#XMLHttpRequest"><span class="toc-text">XMLHttpRequest</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#题目"><span class="toc-text">题目</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#CSS"><span class="toc-text">CSS</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#css单位"><span class="toc-text">css单位</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#translate和使用绝对定位的区别"><span class="toc-text">translate和使用绝对定位的区别</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#BFC"><span class="toc-text">BFC</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#清理浮动"><span class="toc-text">清理浮动</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#选择器优先级"><span class="toc-text">选择器优先级</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#垂直居中"><span class="toc-text">垂直居中</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#HTML"><span class="toc-text">HTML</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#meta标签"><span class="toc-text">meta标签</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#网络"><span class="toc-text">网络</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#从输入URL到页面加载发生了什么"><span class="toc-text">从输入URL到页面加载发生了什么</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#常用状态码"><span class="toc-text">常用状态码</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#网络七层协议"><span class="toc-text">网络七层协议</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#HTTP"><span class="toc-text">HTTP</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#HTTPS"><span class="toc-text">HTTPS</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#跨域"><span class="toc-text">跨域</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#框架"><span class="toc-text">框架</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#单页应用优缺点"><span class="toc-text">单页应用优缺点</span></a></li></ol></li></ol>
    </div>
  </section>


  


</aside>



        
        
          <!--此文件用来存放一些不方便取值的变量-->
<!--思路大概是将值藏到重加载的区域内-->

<script>
  window.pdata={}
  pdata.ispage=true;
  pdata.postTitle="前端面试题";
  pdata.commentPath="";
  pdata.commentPlaceholder="";

  var l_header=document.getElementById("l_header");
  
  l_header.classList.remove("show");
  
</script>

        
      </div>
      
  
  <footer class="footer clearfix">
    <br><br>
    
      
        <div class="aplayer-container">
          


        </div>
      
    
      
        <br>
        <div class="social-wrapper">
          
            
              <a href="mailto:alannum3@qq.com"
                class="social fas fa-envelope flat-btn"
                target="_blank"
                rel="external nofollow noopener noreferrer">
                
              </a>
            
          
            
              <a href="https://github.com/3Alan"
                class="social fab fa-github flat-btn"
                target="_blank"
                rel="external nofollow noopener noreferrer">
                
              </a>
            
          
            
              <a href="tencent://message/?uin=1540703192"
                class="social fab fa-qq flat-btn"
                target="_blank"
                rel="external nofollow noopener noreferrer">
                
              </a>
            
          
            
          
            
          
        </div>
      
    
      
        <div><p>博客内容遵循 <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh" target="_blank" rel="noopener">署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 协议</a></p>
</div>
      
    
      
        <div class='copyright'>
        <p><a href="https://alanwang.site/">Copyright © 2020 Alan</a></p>

        </div>
      
    
  </footer>


      <a id="s-top" class="fas fa-arrow-up fa-fw" href='javascript:void(0)'></a>
    </div>
  </div>
  <div>
    <script>
window.volantis={};
window.volantis.loadcss=document.getElementById("loadcss");
/********************脚本懒加载函数********************************/
function loadScript(src, cb) {
var HEAD = document.getElementsByTagName('head')[0] || document.documentElement;
var script = document.createElement('script');
script.setAttribute('type','text/javascript');
if (cb) script.onload = cb;
script.setAttribute('src', src);
HEAD.appendChild(script);
}
//https://github.com/filamentgroup/loadCSS
var loadCSS = function( href, before, media, attributes ){
	var doc = window.document;
	var ss = doc.createElement( "link" );
	var ref;
	if( before ){
		ref = before;
	}
	else {
		var refs = ( doc.body || doc.getElementsByTagName( "head" )[ 0 ] ).childNodes;
		ref = refs[ refs.length - 1];
	}
	var sheets = doc.styleSheets;
	if( attributes ){
		for( var attributeName in attributes ){
			if( attributes.hasOwnProperty( attributeName ) ){
				ss.setAttribute( attributeName, attributes[attributeName] );
			}
		}
	}
	ss.rel = "stylesheet";
	ss.href = href;
	ss.media = "only x";
	function ready( cb ){
		if( doc.body ){
			return cb();
		}
		setTimeout(function(){
			ready( cb );
		});
	}
	ready( function(){
		ref.parentNode.insertBefore( ss, ( before ? ref : ref.nextSibling ) );
	});
	var onloadcssdefined = function( cb ){
		var resolvedHref = ss.href;
		var i = sheets.length;
		while( i-- ){
			if( sheets[ i ].href === resolvedHref ){
				return cb();
			}
		}
		setTimeout(function() {
			onloadcssdefined( cb );
		});
	};
	function loadCB(){
		if( ss.addEventListener ){
			ss.removeEventListener( "load", loadCB );
		}
		ss.media = media || "all";
	}
	if( ss.addEventListener ){
		ss.addEventListener( "load", loadCB);
	}
	ss.onloadcssdefined = onloadcssdefined;
	onloadcssdefined( loadCB );
	return ss;
};
</script>
<script>
  
  loadCSS("https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@5.14/css/all.min.css", window.volantis.loadcss);
  
  
  
  
  loadCSS("https://cdn.jsdelivr.net/npm/highlight.js@10.0.3/styles/atom-one-dark.min.css", window.volantis.loadcss);
  
</script>
<!-- required -->

<script src="https://cdn.jsdelivr.net/npm/jquery@3.5/dist/jquery.min.js"></script>

<script>
  function pjax_fancybox() {
    $(".md .gallery").find("img").each(function () { //渲染 fancybox
      var element = document.createElement("a"); // a 标签
      $(element).attr("class", "fancybox");
      $(element).attr("pjax-fancybox", "");  // 过滤 pjax
      $(element).attr("href", $(this).attr("src"));
      if ($(this).attr("data-original")) {
        $(element).attr("href", $(this).attr("data-original"));
      }
      $(element).attr("data-fancybox", "images");
      var caption = "";   // 描述信息
      if ($(this).attr('alt')) {  // 判断当前页面是否存在描述信息
        $(element).attr('data-caption', $(this).attr('alt'));
        caption = $(this).attr('alt');
      }
      var div = document.createElement("div");
      $(div).addClass("fancybox");
      $(this).wrap(div); // 最外层套 div ，其实主要作用还是 class 样式
      var span = document.createElement("span");
      $(span).addClass("image-caption");
      $(span).text(caption); // 加描述
      $(this).after(span);  // 再套一层描述
      $(this).wrap(element);  // 最后套 a 标签
    })
    $(".md .gallery").find("img").fancybox({
      selector: '[data-fancybox="images"]',
      hash: false,
      loop: false,
      closeClick: true,
      helpers: {
        overlay: {closeClick: true}
      },
      buttons: [
        "zoom",
        "close"
      ]
    });
  };
  function SCload_fancybox() {
    if ($(".md .gallery").find("img").length == 0) return;
    loadCSS("https://cdn.jsdelivr.net/npm/@fancyapps/fancybox@3.5.7/dist/jquery.fancybox.min.css", document.getElementById("loadcss"));
    setTimeout(function() {
      loadScript('https://cdn.jsdelivr.net/gh/fancyapps/fancybox@3.5.7/dist/jquery.fancybox.min.js', pjax_fancybox)
    }, 1);
  };
  $(function () {
    SCload_fancybox();
  });
</script>


<!-- internal -->

  
  
  
    
<script src="https://cdn.jsdelivr.net/npm/jquery-backstretch@2.1.18/jquery.backstretch.min.js"></script>

    <script type="text/javascript">
        var imgs=["https://gitee.com/alannum3/MyImages/raw/master/img/triangles-3321900_1920.png", "https://gitee.com/alannum3/MyImages/raw/master/img/img-8e2c27a1a7510d3b73317e96ec2f4b72.jpg", "https://gitee.com/alannum3/MyImages/raw/master/img/img-2da8b0f6ac052b7b9402dea246a56701.jpg", "https://gitee.com/alannum3/MyImages/raw/master/img/img-389c21fac02e9980506b6f7ff7f130f6.jpg", "https://gitee.com/alannum3/MyImages/raw/master/img/img-b7f5935e94f6deda4fda5e2820c48bef.jpg", "https://gitee.com/alannum3/MyImages/raw/master/img/img-3c991ba94430b0ddf426d9062bbea403.jpg", "https://cdn.jsdelivr.net/gh/volantis-x/cdn-wallpaper-minimalist/2020/002.jpg", "https://cdn.jsdelivr.net/gh/volantis-x/cdn-wallpaper-minimalist/2020/003.jpg", "https://cdn.jsdelivr.net/gh/volantis-x/cdn-wallpaper-minimalist/2020/004.jpg", "https://cdn.jsdelivr.net/gh/volantis-x/cdn-wallpaper-minimalist/2020/005.jpg", "https://cdn.jsdelivr.net/gh/volantis-x/cdn-wallpaper-minimalist/2020/006.jpg", "https://cdn.jsdelivr.net/gh/volantis-x/cdn-wallpaper-minimalist/2020/012.jpg", "https://cdn.jsdelivr.net/gh/volantis-x/cdn-wallpaper-minimalist/2020/016.jpg", "https://cdn.jsdelivr.net/gh/volantis-x/cdn-wallpaper-minimalist/2020/019.jpg", "https://cdn.jsdelivr.net/gh/volantis-x/cdn-wallpaper-minimalist/2020/025.jpg", "https://cdn.jsdelivr.net/gh/volantis-x/cdn-wallpaper-minimalist/2020/033.jpg", "https://cdn.jsdelivr.net/gh/volantis-x/cdn-wallpaper-minimalist/2020/034.jpg", "https://cdn.jsdelivr.net/gh/volantis-x/cdn-wallpaper-minimalist/2020/035.jpg", "https://cdn.jsdelivr.net/gh/volantis-x/cdn-wallpaper-minimalist/2020/038.jpg", "https://cdn.jsdelivr.net/gh/volantis-x/cdn-wallpaper-minimalist/2020/039.jpg", "https://cdn.jsdelivr.net/gh/volantis-x/cdn-wallpaper-minimalist/2020/042.jpg", "https://cdn.jsdelivr.net/gh/volantis-x/cdn-wallpaper-minimalist/2020/046.jpg", "https://cdn.jsdelivr.net/gh/volantis-x/cdn-wallpaper-minimalist/2020/051.jpg", "https://cdn.jsdelivr.net/gh/volantis-x/cdn-wallpaper-minimalist/2020/052.jpg", "https://cdn.jsdelivr.net/gh/volantis-x/cdn-wallpaper-minimalist/2020/054.jpg", "https://cdn.jsdelivr.net/gh/volantis-x/cdn-wallpaper-minimalist/2020/056.jpg"];
        if ('true' == 'true') {
          function shuffle(arr){
            /*From countercurrent-time*/
            var n = arr.length;
            while(n--) {
              var index = Math.floor(Math.random() * n);
              var temp = arr[index];
              arr[index] = arr[n];
              arr[n] = temp;
            }
          }
          shuffle(imgs);
        }
	  function Pjax_backstretch(){
        
          $.backstretch(
            imgs,
          {
            duration: "20000",
            fade: "1500"
          });
        
	  }
	  loadScript("https://cdn.jsdelivr.net/npm/jquery-backstretch@2.1.18/jquery.backstretch.min.js",Pjax_backstretch)
    </script>
  



  
  

  <div id="rightmenu-wrapper">
    <ul class="list-v rightmenu" id="rightmenu-content">
      <li class='option'>
        <a class='vlts-menu opt fix-cursor-default' id='menu-copy-text' onclick="document.execCommand('copy')"><i class='fa fa-copy fa-fw'></i>复制文本</a>
        <hr id='hr-text'>
        <a class='vlts-menu opt fix-cursor-default' id='menu-copy-href'><i class='fa fa-link fa-fw'></i>复制链接</a>
        <a class='vlts-menu opt fix-cursor-default' id='menu-open-href'><i class='fa fa-external-link-square-alt fa-fw'></i>在新标签页打开</a>
        <hr id='hr-href'>
        <a class='vlts-menu opt fix-cursor-default' id='menu-copy-src'><i class='fa fa-image fa-fw'></i>复制图片地址</a>
        <hr id='hr-src'>
      </li>
      
        <li class='navigation'>
          <a class='nav icon-only fix-cursor-default' onclick='history.back()'><i class='fa fa-arrow-left fa-fw'></i></a>
          <a class='nav icon-only fix-cursor-default' onclick='history.forward()'><i class='fa fa-arrow-right fa-fw'></i></a>
          <a class='nav icon-only fix-cursor-default' onclick='window.location.reload()'><i class='fa fa-redo fa-fw'></i></a>
          <a class='nav icon-only fix-cursor-default' href='/'><i class='fa fa-home fa-fw'></i></a>
        </li>
      
      <hr>
      
        
      
        
      
    </ul>
  </div>

  <script>
    window.document.oncontextmenu = function (event) {
      if (event.ctrlKey) return true;
      if (/Android|webOS|BlackBerry/i.test(navigator.userAgent)) return true;
      return popMenu(event);
    };
    document.addEventListener("click", function (event) {
      var mymenu = document.getElementById('rightmenu-wrapper');
      mymenu.style.display = "none";
    });
    function popMenu(event) {
      var mymenu = document.getElementById('rightmenu-wrapper');
      var menuContent = document.getElementById('rightmenu-content');
      var screenWidth = document.documentElement.clientWidth || document.body.clientWidth;
      var screenHeight = document.documentElement.clientHeight || document.body.clientHeight;
      mymenu.style.left = event.clientX + "px"; // 获取鼠标位置
      mymenu.style.top = event.clientY + "px";
      mymenu.style.display = 'block';
      if (event.clientX * 2 > screenWidth) {
        menuContent.classList.add('left');
      } else {
        menuContent.classList.remove('left');
      }
      if (event.clientY * 2 > screenHeight) {
        menuContent.classList.add('top');
      } else {
        menuContent.classList.remove('top');
      }

      let hrText = document.getElementById('hr-text');
      let hrSrc = document.getElementById('hr-src');
      let hrHref = document.getElementById('hr-href');

      // 选中图片
      let copySrc = document.getElementById('menu-copy-src');
      if (copySrc != undefined) {
        if (event.target.currentSrc) {
          copySrc.style.display = 'block';
          copySrc.addEventListener("click", function (e) {
            copyString(event.target.currentSrc);
          },{once: true});
          hrSrc.style.display = 'block';
        } else {
          copySrc.style.display = 'none';
          hrSrc.style.display = 'none';
        }
      }

      // 选中按钮
      // 判断是不是按钮
      let href = '';
      if (event.path) {
        for (i = 0; i < event.path.length; i++) {
          if (event.path[i].href != undefined && event.path[i].href.length > 0) {
            href = event.path[i].href;
          }
        }
      }

      let copyText = document.getElementById('menu-copy-text');
      copyText.style.display = 'none';
      hrText.style.display = 'none';
      if (href.length == 0) {
        // 选中文本
        if (window.getSelection().toString()) {
          if ('true' == 'true') {
            copyText.style.display = 'block';
            hrText.style.display = 'block';
          }
        }
      }

      // 在新标签页打开
      let openHref = document.getElementById('menu-open-href');
      if (openHref != undefined) {
        if (href.length > 0) {
          openHref.style.display = 'block';
          openHref.addEventListener("click", function (e) {
            window.open(href);
          },{once: true});
          hrHref.style.display = 'block';
        } else {
          openHref.style.display = 'none';
          hrHref.style.display = 'none';
        }
      }
      // 复制链接
      let copyHref = document.getElementById('menu-copy-href');
      if (copyHref != undefined) {
        if (href.length > 0) {
          copyHref.style.display = 'block';
          copyHref.addEventListener("click", function (e) {
            copyString(href);
          },{once: true});
        } else {
          copyHref.style.display = 'none';
        }
      }

      // 有音乐播放器
      if ('false' == true && ('home,music'||[]).includes('music')) {
        // 如果有aplayer，初始化一下
      	try {
      		checkAPlayer();
          updateTitle();
      	} catch (error) {
      		console.log(error);
      	}
      }

      return false; // 该行代码确保系统自带的右键菜单不会被调出
    }
    function hideMenu() {
      document.getElementById('rightmenu-wrapper').style.display = 'none';
    }
    function copyString(str) {
      const input = document.createElement('input');
      input.setAttribute('readonly', 'readonly');
      document.body.appendChild(input);
      input.setAttribute('value', str);
      input.select();
      document.execCommand('copy');
      document.body.removeChild(input);
    }
    document.execCommand('click');
  </script>







  <script defer src="https://cdn.jsdelivr.net/npm/vanilla-lazyload@17.1.0/dist/lazyload.min.js"></script>
<script>
  // https://www.npmjs.com/package/vanilla-lazyload
  // Set the options globally
  // to make LazyLoad self-initialize
  window.lazyLoadOptions = {
    elements_selector: ".lazyload",
    threshold: 0
  };
  // Listen to the initialization event
  // and get the instance of LazyLoad
  window.addEventListener(
    "LazyLoad::Initialized",
    function (event) {
      window.lazyLoadInstance = event.detail.instance;
    },
    false
  );
  document.addEventListener('DOMContentLoaded', function () {
    lazyLoadInstance.update();
  });
  document.addEventListener('pjax:complete', function () {
    lazyLoadInstance.update();
  });
</script>




  
  
    <script>
      window.FPConfig = {
        delay: 0,
        ignoreKeywords: [],
        maxRPS: 5,
        hoverDelay: 25
      };
    </script>
    <script defer src="https://cdn.jsdelivr.net/gh/gijo-varghese/flying-pages@2.1.2/flying-pages.min.js"></script>
  




  <script src="https://cdn.jsdelivr.net/npm/clipboard@2/dist/clipboard.min.js"></script>
<script>
    var clipboard = new ClipboardJS('.btn-copy', {
        target: function (trigger) {
            return trigger.nextElementSibling
        }
    });
    function wait(callback, seconds) {
        var timelag = null;
        timelag = window.setTimeout(callback, seconds)
    }
    function pjax_initCopyCode() {
		if($(".highlight .code pre").length+$(".article pre code").length==0)return;
        var copyHtml = '';
        copyHtml += '<button class="btn-copy" data-clipboard-snippet="">';
        copyHtml += '<i class="fas fa-copy"></i><span>COPY</span>';
        copyHtml += '</button>';
        $(".highlight .code pre").before(copyHtml);
        $(".article pre code").before(copyHtml);
        clipboard.off('success').on('success', function (e) {
            let $btn = $(e.trigger);
            $btn.addClass('copied');
            let $icon = $($btn.find('i'));
            $icon.removeClass('fa-copy');
            $icon.addClass('fa-check-circle');
            let $span = $($btn.find('span'));
            $span[0].innerText = 'COPIED';
            wait(function () {
                $icon.removeClass('fa-check-circle');
                $icon.addClass('fa-copy');
                $span[0].innerText = 'COPY'
            }, 2000)
        });
        clipboard.off('error').on('error', function (e) {
            e.clearSelection();
            let $btn = $(e.trigger);
            $btn.addClass('copy-failed');
            let $icon = $($btn.find('i'));
            $icon.removeClass('fa-copy');
            $icon.addClass('fa-times-circle');
            let $span = $($btn.find('span'));
            $span[0].innerText = 'COPY FAILED';
            wait(function () {
                $icon.removeClass('fa-times-circle');
                $icon.addClass('fa-copy');
                $span[0].innerText = 'COPY'
            }, 2000)
        })
    }
    $(function () {
        pjax_initCopyCode()
    });
</script>








  
  
<script src="https://cdn.jsdelivr.net/npm/valine@1.4/dist/Valine.min.js"></script>


<script>
  var GUEST_INFO = ['nick', 'mail', 'link'];
  var meta = 'nick,mail,link'.split(',').filter(function (item) {
    return GUEST_INFO.indexOf(item) > -1
  });
  var REQUIRED_FIELDS = ['nick', 'mail', 'link'];
  var requiredFields = 'nick,mail'.split(',').filter(function (item) {
    return REQUIRED_FIELDS.indexOf(item) > -1
  });

  function emoji(path, idx, ext) {
    return path + "/" + path + "-" + idx + "." + ext;
  }

  var emojiMaps = {};
  for (var i = 1; i <= 54; i++) {
    emojiMaps['tieba-' + i] = emoji('tieba', i, 'png');
  }
  for (var i = 1; i <= 101; i++) {
    emojiMaps['qq-' + i] = emoji('qq', i, 'gif');
  }
  for (var i = 1; i <= 116; i++) {
    emojiMaps['aru-' + i] = emoji('aru', i, 'gif');
  }
  for (var i = 1; i <= 125; i++) {
    emojiMaps['twemoji-' + i] = emoji('twemoji', i, 'png');
  }
  for (var i = 1; i <= 4; i++) {
    emojiMaps['weibo-' + i] = emoji('weibo', i, 'png');
  }

  function pjax_valine() {
    if(!document.querySelectorAll("#valine_container")[0])return;

    let pagePlaceholder = pdata.commentPlaceholder || "快来评论吧~";

    let path = pdata.commentPath;
    if (path.length == 0) {
      let defaultPath = '';
      path = defaultPath || decodeURI(window.location.pathname);
    }

    var valine = new Valine();
    valine.init({
      el: '#valine_container',
      meta: meta,
      placeholder: pagePlaceholder,
      path: path,
      appId: "aowgxqHE7wB4I8HLWqC4c4Ut-MdYXbMMI",
      appKey: "37wkY4T3cM7nV2UWV7EIEHPy",
      pageSize: '10',
      avatar: 'robohash',
      lang: 'zh-cn',
      visitor: 'true',
      highlight: 'true',
      mathJax: 'false',
      enableQQ: 'true',
      recordIP: 'false',
      requiredFields: requiredFields,
      emojiCDN: 'https://cdn.jsdelivr.net/gh/volantis-x/cdn-emoji/valine/',
      emojiMaps: emojiMaps
    })
  }

  $(function () {
    pjax_valine();
  });
</script>




  <script defer src="https://cdn.jsdelivr.net/gh/volantis-x/cdn-busuanzi@2.3/js/busuanzi.pure.mini.js" data-pjax></script>


  
<script src="https://cdn.jsdelivr.net/npm/hexo-theme-volantis@4.1.5/source/js/app.min.js"></script>




  
  
<script src="https://cdn.jsdelivr.net/npm/hexo-theme-volantis@4.1.5/source/js/search.min.js"></script>

  


<!-- optional -->

  <script>
const SearchServiceimagePath="https://cdn.jsdelivr.net/gh/volantis-x/cdn-volantis@master/img/";
const ROOT =  ("/" || "/").endsWith('/') ? ("/" || "/") : ("//" || "/" );
function listenSearch(){
  
    customSearch = new HexoSearch({
      imagePath: SearchServiceimagePath
    });
  
}
document.addEventListener("DOMContentLoaded", listenSearch);

</script>







  
<script src="https://cdn.jsdelivr.net/gh/volantis-x/cdn-volantis@2/js/comment_typing.js"></script>




  
    
<script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@9.18.1/build/highlight.min.js"></script>

    <script>hljs.initHighlightingOnLoad();</script>
  



  <script defer>

  const LCCounter = {
    app_id: 'aowgxqHE7wB4I8HLWqC4c4Ut-MdYXbMMI',
    app_key: '37wkY4T3cM7nV2UWV7EIEHPy',
    custom_api_server: '',

    // 查询存储的记录
    getRecord(Counter, url, title) {
      return new Promise(function (resolve, reject) {
        Counter('get', '/classes/Counter?where=' + encodeURIComponent(JSON.stringify({url})))
          .then(resp => resp.json())
          .then(({results, code, error}) => {
            if (code === 401) {
              throw error;
            }
            if (results && results.length > 0) {
              var record = results[0];
              resolve(record);
            } else {
              Counter('post', '/classes/Counter', {url, title: title, times: 0})
                .then(resp => resp.json())
                .then((record, error) => {
                  if (error) {
                    throw error;
                  }
                  resolve(record);
                }).catch(error => {
                console.error('Failed to create', error);
                reject(error);
              });
            }
          }).catch((error) => {
          console.error('LeanCloud Counter Error:', error);
          reject(error);
        });
      })
    },

    // 发起自增请求
    increment(Counter, incrArr) {
      return new Promise(function (resolve, reject) {
        Counter('post', '/batch', {
          "requests": incrArr
        }).then((res) => {
          res = res.json();
          if (res.error) {
            throw res.error;
          }
          resolve(res);
        }).catch((error) => {
          console.error('Failed to save visitor count', error);
          reject(error);
        });
      });
    },

    // 构建自增请求体
    buildIncrement(objectId) {
      return {
        "method": "PUT",
        "path": `/1.1/classes/Counter/${ objectId }`,
        "body": {
          "times": {
            '__op': 'Increment',
            'amount': 1
          }
        }
      }
    },

    // 校验是否为有效的 UV
    validUV() {
      var key = 'LeanCloudUVTimestamp';
      var flag = localStorage.getItem(key);
      if (flag) {
        // 距离标记小于 24 小时则不计为 UV
        if (new Date().getTime() - parseInt(flag) <= 86400000) {
          return false;
        }
      }
      localStorage.setItem(key, new Date().getTime().toString());
      return true;
    },

    addCount(Counter) {
      var enableIncr = '' === 'true' && window.location.hostname !== 'localhost';
      enableIncr = true;
      var getterArr = [];
      var incrArr = [];
      // 请求 PV 并自增
      var pvCtn = document.querySelector('#lc-sv');
      if (pvCtn || enableIncr) {
        var pvGetter = this.getRecord(Counter, 'https://alanwang.site' + '/#lc-sv', 'Visits').then((record) => {
          incrArr.push(this.buildIncrement(record.objectId))
          var eles = document.querySelectorAll('#lc-sv #number');
          if (eles.length > 0) {
            eles.forEach((el,index,array)=>{
              el.innerText = record.times + 1;
              if (pvCtn) {
                pvCtn.style.display = 'inline';
              }
            })
          }
        });
        getterArr.push(pvGetter);
      }

      // 请求 UV 并自增
      var uvCtn = document.querySelector('#lc-uv');
      if (uvCtn || enableIncr) {
        var uvGetter = this.getRecord(Counter, 'https://alanwang.site' + '/#lc-uv', 'Visitors').then((record) => {
          var vuv = this.validUV();
          vuv && incrArr.push(this.buildIncrement(record.objectId))
          var eles = document.querySelectorAll('#lc-uv #number');
          if (eles.length > 0) {
            eles.forEach((el,index,array)=>{
              el.innerText = record.times + (vuv ? 1 : 0);
              if (uvCtn) {
                uvCtn.style.display = 'inline';
              }
            })
          }
        });
        getterArr.push(uvGetter);
      }

      // 请求文章的浏览数，如果是当前页面就自增
      var allPV = document.querySelectorAll('#lc-pv');
      if (allPV.length > 0 || enableIncr) {
        for (i = 0; i < allPV.length; i++) {
          let pv = allPV[i];
          let title = pv.getAttribute('data-title');
          var url = 'https://alanwang.site' + pv.getAttribute('data-path');
          if (url) {
            var viewGetter = this.getRecord(Counter, url, title).then((record) => {
              // 是当前页面就自增
              let curPath = window.location.pathname;
              if (curPath.includes('index.html')) {
                curPath = curPath.substring(0, curPath.lastIndexOf('index.html'));
              }
              if (pv.getAttribute('data-path') == curPath) {
                incrArr.push(this.buildIncrement(record.objectId));
              }
              if (pv) {
                var ele = pv.querySelector('#lc-pv #number');
                if (ele) {
                  if (pv.getAttribute('data-path') == curPath) {
                    ele.innerText = (record.times || 0) + 1;
                  } else {
                    ele.innerText = record.times || 0;
                  }
                  pv.style.display = 'inline';
                }
              }
            });
            getterArr.push(viewGetter);
          }
        }
      }

      // 如果启动计数自增，批量发起自增请求
      if (enableIncr) {
        Promise.all(getterArr).then(() => {
          incrArr.length > 0 && this.increment(Counter, incrArr);
        })
      }

    },


    fetchData(api_server) {
      var Counter = (method, url, data) => {
        return fetch(`${ api_server }/1.1${ url }`, {
          method,
          headers: {
            'X-LC-Id': this.app_id,
            'X-LC-Key': this.app_key,
            'Content-Type': 'application/json',
          },
          body: JSON.stringify(data)
        });
      };
      this.addCount(Counter);
    },

    refreshCounter() {
      var api_server = this.app_id.slice(-9) !== '-MdYXbMMI' ? this.custom_api_server : `https://${ this.app_id.slice(0, 8).toLowerCase() }.api.lncldglobal.com`;
      if (api_server) {
        this.fetchData(api_server);
      } else {
        fetch('https://app-router.leancloud.cn/2/route?appId=' + this.app_id)
          .then(resp => resp.json())
          .then(({api_server}) => {
            this.fetchData('https://' + api_server);
          });
      }
    }

  };

  LCCounter.refreshCounter();

  document.addEventListener('pjax:complete', function () {
    LCCounter.refreshCounter();
  });
</script>




  <script>
const rootElement = document.documentElement;
const darkModeStorageKey = "user-color-scheme";
const rootElementDarkModeAttributeName = "data-user-color-scheme";

const setLS = (k, v) => {
  try {
    localStorage.setItem(k, v);
  } catch (e) {
    console.log(e.message);
  }
};

const removeLS = (k) => {
  try {
    localStorage.removeItem(k);
  } catch (e) {
    console.log(e.message);
  }
};

const getLS = (k) => {
  try {
    return localStorage.getItem(k);
  } catch (e) {
    console.log(e.message);
    return null;
  }
};

const getModeFromCSSMediaQuery = () => {
  return window.matchMedia("(prefers-color-scheme: dark)").matches
    ? "dark"
    : "light";
};

const resetRootDarkModeAttributeAndLS = () => {
  rootElement.removeAttribute(rootElementDarkModeAttributeName);
  removeLS(darkModeStorageKey);
};

const validColorModeKeys = {
  dark: true,
  light: true,
};

const applyCustomDarkModeSettings = (mode) => {
  const currentSetting = mode || getLS(darkModeStorageKey);

  if (currentSetting === getModeFromCSSMediaQuery()) {
    resetRootDarkModeAttributeAndLS();
  } else if (validColorModeKeys[currentSetting]) {
    rootElement.setAttribute(rootElementDarkModeAttributeName, currentSetting);
  } else {
    resetRootDarkModeAttributeAndLS();
  }
};

const invertDarkModeObj = {
  dark: "light",
  light: "dark",
};

/**
 * get target mode
 */
const toggleCustomDarkMode = () => {
  let currentSetting = getLS(darkModeStorageKey);

  if (validColorModeKeys[currentSetting]) {
    currentSetting = invertDarkModeObj[currentSetting];
  } else if (currentSetting === null) {
    currentSetting = invertDarkModeObj[getModeFromCSSMediaQuery()];
  } else {
    return;
  }
  setLS(darkModeStorageKey, currentSetting);
  return currentSetting;
};

/**
 * bind click event for toggle button
 */
function bindToggleButton() {
	var btn=$("#wrapper .toggle-mode-btn");
    btn.on('click',(e) => {
      const mode = toggleCustomDarkMode();
      applyCustomDarkModeSettings(mode);
    });
}

applyCustomDarkModeSettings();
document.addEventListener("DOMContentLoaded", bindToggleButton);
document.addEventListener("pjax:success", bindToggleButton);

</script>





<!-- baidu Analytics -->
<script>
var _hmt = _hmt || [];
(function() {
  setTimeout(function() {
	var hm = document.createElement("script");
	hm.src = "https://hm.baidu.com/hm.js?ff1856f91533fcea9c8c2fe5a1b06fe7";
	hm.defer=true;
	var s = document.getElementsByTagName("script")[0];
	s.parentNode.insertBefore(hm, s);
  }, 5000);
})();
</script>


<script>
function listennSidebarTOC() {
  const navItems = document.querySelectorAll(".toc li");
  if (!navItems.length) return;
  const sections = [...navItems].map((element) => {
    const link = element.querySelector(".toc-link");
    const target = document.getElementById(
      decodeURI(link.getAttribute("href")).replace("#", "")
    );
    link.addEventListener("click", (event) => {
      event.preventDefault();
      window.scrollTo({
		top: target.offsetTop + 100,
		
		behavior: "smooth"
		
	  });
    });
    return target;
  });

  function activateNavByIndex(target) {
    if (target.classList.contains("active-current")) return;

    document.querySelectorAll(".toc .active").forEach((element) => {
      element.classList.remove("active", "active-current");
    });
    target.classList.add("active", "active-current");
    let parent = target.parentNode;
    while (!parent.matches(".toc")) {
      if (parent.matches("li")) parent.classList.add("active");
      parent = parent.parentNode;
    }
  }

  function findIndex(entries) {
    let index = 0;
    let entry = entries[index];
    if (entry.boundingClientRect.top > 0) {
      index = sections.indexOf(entry.target);
      return index === 0 ? 0 : index - 1;
    }
    for (; index < entries.length; index++) {
      if (entries[index].boundingClientRect.top <= 0) {
        entry = entries[index];
      } else {
        return sections.indexOf(entry.target);
      }
    }
    return sections.indexOf(entry.target);
  }

  function createIntersectionObserver(marginTop) {
    marginTop = Math.floor(marginTop + 10000);
    let intersectionObserver = new IntersectionObserver(
      (entries, observe) => {
        let scrollHeight = document.documentElement.scrollHeight + 100;
        if (scrollHeight > marginTop) {
          observe.disconnect();
          createIntersectionObserver(scrollHeight);
          return;
        }
        let index = findIndex(entries);
        activateNavByIndex(navItems[index]);
      },
      {
        rootMargin: marginTop + "px 0px -100% 0px",
        threshold: 0,
      }
    );
    sections.forEach((element) => {
      element && intersectionObserver.observe(element);
    });
  }
  createIntersectionObserver(document.documentElement.scrollHeight);
}

document.addEventListener("DOMContentLoaded", listennSidebarTOC);
document.addEventListener("pjax:success", listennSidebarTOC);
</script>

<!-- more -->




    
      


<script src="https://cdn.jsdelivr.net/npm/pjax@0.2.8/pjax.min.js"></script>


<script>
    var pjax;
    document.addEventListener('DOMContentLoaded', function () {
      pjax = new Pjax({
        elements: 'a[href]:not([href^="#"]):not([href="javascript:void(0)"]):not([pjax-fancybox])',
        selectors: [
          "title",
          "#l_cover",
          "#pjax-container",
          "#pjax-header-nav-list"
        ],
        cacheBust: false,   // url 地址追加时间戳，用以避免浏览器缓存
        timeout: 5000
      });
    });

    document.addEventListener('pjax:send', function (e) {
      //window.stop(); // 相当于点击了浏览器的停止按钮

      try {
        var currentUrl = window.location.pathname;
        var targetUrl = e.triggerElement.href;
        var banUrl = [""];
        if (banUrl[0] != "") {
          banUrl.forEach(item => {
            if(currentUrl.indexOf(item) != -1 || targetUrl.indexOf(item) != -1) {
              window.location.href = targetUrl;
            }
          });
        }
      } catch (error) {}

      window.subData = null; // 移除标题（用于一二级导航栏切换处）
      if (typeof $.fancybox != "undefined") {
        $.fancybox.close();    // 关闭弹窗
      }
      volantis.$switcher.removeClass('active'); // 关闭移动端激活的搜索框
      volantis.$header.removeClass('z_search-open'); // 关闭移动端激活的搜索框
      volantis.$wrapper.removeClass('sub'); // 跳转页面时关闭二级导航

      // 解绑事件 避免重复监听
      volantis.$topBtn.unbind('click');
      $('.menu a').unbind('click');
      $(window).unbind('resize');
      $(window).unbind('scroll');
      $(document).unbind('scroll');
      $(document).unbind('click');
      $('body').unbind('click');
	  
    });

    document.addEventListener('pjax:complete', function () {
      // 关于百度统计对 SPA 页面的处理：
      // 方案一：百度统计>管理>单页应用设置中，打开开启按钮即可对SPA进行统计。 https://tongji.baidu.com/web/help/article?id=324
      // 方案二：取消注释下列代码。 https://tongji.baidu.com/web/help/article?id=235
      // 
      // _hmt.push(['_trackPageview', document.location.pathname]);
      // 

      // 关于谷歌统计对 SPA 页面的处理：
      // 当应用以动态方式加载内容并更新地址栏中的网址时，也应该更新通过 gtag.js 存储的网页网址。
      // https://developers.google.cn/analytics/devguides/collection/gtagjs/single-page-applications?hl=zh-cn
      

      $('.nav-main').find('.list-v').not('.menu-phone').removeAttr("style",""); // 移除小尾巴的移除
      $('.menu-phone.list-v').removeAttr("style",""); // 移除小尾巴的移除
      $('script[data-pjax], .pjax-reload script').each(function () {
        $(this).parent().append($(this).remove());
      });
      try{
          if (typeof $.fancybox == "undefined") {
            SCload_fancybox();
          } else {
            pjax_fancybox();
          }
        
		  Pjax_backstretch()
        
        
        document.querySelectorAll('pre code').forEach((block) => {
          hljs.highlightBlock(block);
        });
        
        
        
        
          pjax_initCopyCode();
        
        
          pjax_valine();
        
        
        
        
      } catch (e) {
        console.log(e);
      }
	  
    });

    document.addEventListener('pjax:error', function (e) {
	  
      window.location.href = e.triggerElement.href;
    });
</script>

    
  </div>
<script>
            window.imageLazyLoadSetting = {
                isSPA: false,
                processImages: null,
            };
        </script><script>window.addEventListener("load",function(){var t=/\.(gif|jpg|jpeg|tiff|png)$/i,r=/^data:image\/[a-z]+;base64,/;Array.prototype.slice.call(document.querySelectorAll("img[data-original]")).forEach(function(a){var e=a.parentNode;"A"===e.tagName&&(e.href.match(t)||e.href.match(r))&&(e.href=a.dataset.original)})});</script><script>!function(n){n.imageLazyLoadSetting.processImages=i;var e=n.imageLazyLoadSetting.isSPA,r=Array.prototype.slice.call(document.querySelectorAll("img[data-original]"));function i(){e&&(r=Array.prototype.slice.call(document.querySelectorAll("img[data-original]")));for(var t,a=0;a<r.length;a++)0<=(t=(t=r[a]).getBoundingClientRect()).bottom&&0<=t.left&&t.top<=(n.innerHeight||document.documentElement.clientHeight)&&function(){var t,e,n,i,o=r[a];t=o,e=function(){r=r.filter(function(t){return o!==t})},n=new Image,i=t.getAttribute("data-original"),n.onload=function(){t.src=i,e&&e()},n.src=i}()}i(),n.addEventListener("scroll",function(){var t,e;t=i,e=n,clearTimeout(t.tId),t.tId=setTimeout(function(){t.call(e)},500)})}(this);</script></body>
</html>
